/*
File: Game.cpp
Author: Zachary Murray
Purpose: Game class controls the game state machine
*/

#include "Game.h"

#include "../SGD Wrappers/SGD_AudioManager.h"
#include "../SGD Wrappers/SGD_GraphicsManager.h"
#include "../SGD Wrappers/SGD_InputManager.h"
#include "../SGD Wrappers/SGD_String.h"
#include "../SGD Wrappers/SGD_Event.h"
#include "../SGD Wrappers/SGD_EventManager.h"

#include "IGameState.h"
#include "MainMenuState.h"
#include "GamePlayState.h"
#include "HowToState.h"
#include "OptionsState.h"
#include "CreditsState.h"
#include "LoadState.h"
#include "BitmapFont.h"
#include "SoundPlayer.h"
#include "Player.h"

#include <ctime>
#include <cstdlib>
#include <cassert>

#define WIN32_LEAN_AND_MEAN
#include <Windows.h>

//Singleton
Game* Game::gameInstance = nullptr;

//Allocate the ONE instance and return it
Game* Game::GetInstance()
{
	if (nullptr == gameInstance)
		gameInstance = new Game;

	return gameInstance;
}

//Deallocate the ONE instance
void Game::DeleteInstance()
{
	delete gameInstance;
	gameInstance = nullptr;
}

//Initialize the game
bool Game::Initialize(int w, int h)
{
	srand((unsigned int)time(nullptr));
	rand();

	//Store the SGD singletons
	audio = SGD::AudioManager::GetInstance();
	graphics = SGD::GraphicsManager::GetInstance();
	input = SGD::InputManager::GetInstance();

	//Initialize the Event Manager
	events = SGD::EventManager::GetInstance();
	events->Initialize();

	//init the singletons
	if (false == audio->Initialize()
		|| false == graphics->Initialize(false)
		|| false == input->Initialize())
		return false;

	graphics->SetClearColor({ 0,0,0 });

	//Size parameters
	screenWidth = screenWidthhold = w;
	screenHeight = screenHeighthold = h;

	//Allocate and init the font
	font = new BitmapFont;
	font->Initialize("resources/xml/font.fnt");

	//Allocate and init the SoundPlayer
	soundPlayer = new SoundPlayer;
	soundPlayer->Enter();

	ChangeState(MainMenuState::GetInstance());

	gameTime = GetTickCount64();

	return true;
}

//Run the game
int Game::Play()
{
	if (audio->Update() == false
		|| graphics->Update() == false
		|| input->Update() == false)
	{
		return -10;		// abort!
	}

	if (input->IsKeyPressed(SGD::Key::Plus))
		ZoomIn(.1f);

	if (input->IsKeyPressed(SGD::Key::Minus))
		ZoomOut(.1f);

	// Current time
	unsigned long long now = GetTickCount64();
	float elapsedTime = (now - gameTime) / 1000.0f;
	gameTime = now;

	// Cap the elapsed time to 1/8th of a second
	if (elapsedTime > 0.125f)
		elapsedTime = 0.125f;

	if (windowed)
	{
		screenWidth = screenWidthhold;
		screenHeight = screenHeighthold;
		graphics->Resize(SGD::Size{ (float)screenWidth, (float)screenHeight }, windowed);
	}
	else
	{
		screenWidth = 1920;
		screenHeight = 1080;
		graphics->Resize(SGD::Size{ (float)screenWidth, (float)screenHeight }, windowed);
	}

	events->Update();
	// Let the current state handle input
	if (currentState->Input() == false)
		return 1;	// exit success!

	currentState->Update(elapsedTime);

	if (currentState == GamePlayState::GetInstance()
		&& GamePlayState::GetInstance()->GetPlayer() != nullptr
		&& !GamePlayState::GetInstance()->GetMapPause())
	{
		if (!GamePlayState::GetInstance()->GetMapPause())
		SetCameraPos(GamePlayState::GetInstance()->GetPlayer()->GetRect().ComputeCenter()
			- SGD::Size{ ((float)(screenWidth / 2) * (1/zoomLevel)), ((float)(screenHeight / 2) * (1/zoomLevel)) });
	}
	else if (currentState == HowToState::GetInstance()
		&& HowToState::GetInstance()->GetPlayer() != nullptr)
	{
		SetCameraPos(HowToState::GetInstance()->GetPlayer()->GetRect().ComputeCenter()
			- SGD::Size{ ((float)(screenWidth / 2) * (1 / zoomLevel)), ((float)(screenHeight / 2) * (1 / zoomLevel)) });
	}

	currentState->Render();

	return 0;
}

//Shutdown the game, cleaning up memory
void Game::Shutdown()
{
	ChangeState(nullptr);

	font->Terminate();
	delete font;

	soundPlayer->Exit();
	delete soundPlayer;
	soundPlayer = nullptr;

	audio->Terminate();
	audio = nullptr;
	SGD::AudioManager::DeleteInstance();

	graphics->Terminate();
	graphics = nullptr;
	SGD::GraphicsManager::DeleteInstance();

	input->Terminate();
	input = nullptr;
	SGD::InputManager::DeleteInstance();

	events->Terminate();
	events = nullptr;
	SGD::EventManager::DeleteInstance();
}

//Handle changing states
void Game::ChangeState(IGameState* newState)
{
	if (newState == currentState)
		return;

	if (nullptr != currentState)
		currentState->Exit();

	currentState = newState;

	if (nullptr != currentState)
		currentState->Enter();
}

void Game::ZoomOut(float zoom)
{
	zoomLevel = zoomLevel - zoom;
	if (zoomLevel < 0.5f)
		zoomLevel = 0.5f;
	prevZoomLevel = zoomLevel;
}

void Game::ZoomIn(float zoom)
{
	zoomLevel = zoomLevel + zoom;
	if (zoomLevel > 1.f)
		zoomLevel = 1.f;
	prevZoomLevel = zoomLevel;
}